home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Sample Code Update 01⁄96 / Fragment Tool / Sources / DragStuff.c < prev    next >
Encoding:
Text File  |  1995-11-20  |  12.7 KB  |  513 lines  |  [TEXT/MPCC]

  1. /*
  2.     File:        DragStuff.c
  3.  
  4.     Contains:    Drag Manager handlers and associated routines
  5.  
  6.     Written by:    Chris White, Developer Technical Support
  7.     
  8.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  9.     
  10.     Change History (most recent first):
  11.     
  12.                   9/28/95    CW        First release
  13.  
  14. */
  15.  
  16.  
  17. // System includes
  18. #ifndef __DRAG__
  19.     #include <Drag.h>
  20. #endif
  21.  
  22. #ifndef __ERRORS__
  23.     #include <Errors.h>
  24. #endif
  25.  
  26. #ifndef __GESTALT__
  27.     #include <Gestalt.h>
  28. #endif
  29.  
  30. #ifndef __CODEFRAGMENTS__
  31.     #include <CodeFragments.h>
  32. #endif
  33.  
  34. #ifndef __SCRIPT__
  35.     #include <Script.h>
  36. #endif
  37.  
  38. #ifndef __RESOURCES__
  39.     #include <Resources.h>
  40. #endif
  41.  
  42. #ifndef __STDDEF__
  43.     #include <stddef.h>
  44. #endif
  45.  
  46.  
  47.  
  48. // Application includes
  49. #ifndef __FRAGMENTTOOL__
  50.     #include "FragmentTool.h"
  51. #endif
  52.  
  53. #ifndef __PROTOTYPES__
  54.     #include "Prototypes.h"
  55. #endif
  56.  
  57.  
  58. #include "Utilities.h"
  59.  
  60.  
  61.  
  62. static Boolean gHasAcceptableDrag = false;
  63. static Boolean gHasHilitedList = false;
  64.  
  65.  
  66.  
  67.  
  68. static Boolean DragItemsAreAcceptable ( DragReference theDrag );
  69. static Boolean DragIsNotInSourceWindow ( DragReference theDrag );
  70.  
  71.  
  72. static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  73.                                     void *handlerRefCon, DragReference theDrag );
  74. static pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  75.                                     void *handlerRefCon, DragReference theDrag );
  76. static pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, 
  77.                                     DragReference theDrag );
  78. static pascal OSErr SendDataProc ( FlavorType theType,  void *dragSendRefCon,
  79.                                      ItemReference theItemRef, DragReference theDragRef );
  80.  
  81.  
  82.  
  83. //
  84. // InitDragHandlers creates the UPPs for the Drag Manager 
  85. // callback routines _if_ the Drag Manager is available.
  86. //
  87. OSErr InitDragHandlers ( void )
  88. {
  89.     OSErr    theErr = noErr;
  90.     
  91.     if ( gHasDragManager )
  92.     {
  93.         gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( DragTracker );
  94.         gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( DragReceiver );
  95.         gDragSendDataProcUPP = NewDragSendDataProc ( SendDataProc );
  96.     }
  97.     
  98.     return theErr;
  99. }
  100.  
  101.  
  102.  
  103. //
  104. // InstallDragHandlers attaches the tracking and receive handlers to
  105. // one of the application's windows.
  106. //
  107. OSErr InstallDragHandlers ( WindowRef theWindow )
  108. {    
  109.     OSErr     theErr = noErr;
  110.     
  111.     if ( gHasDragManager )
  112.     {
  113.         
  114.         theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil );
  115.     
  116.         if ( theErr == noErr )
  117.         {
  118.             theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil );
  119.             if ( theErr )
  120.                 (void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  121.         }
  122.         
  123.     }
  124.     
  125.     return theErr;
  126. }
  127.  
  128.  
  129.  
  130. //
  131. // RemoveDragHandlers removes the tracking and receive handlers from
  132. // one of the application's windows (usually just prior to disposal).
  133. //
  134. void RemoveDragHandlers ( WindowRef theWindow )
  135. {
  136.     if ( gHasDragManager )
  137.     {
  138.         
  139.         RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow );
  140.         RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  141.         
  142.     }
  143.     
  144.     return;
  145. }
  146.  
  147.  
  148.  
  149. //
  150. // DragItemsAreAcceptable returns true if the contents (data) of
  151. // the drag are acceptable. This is called by the tracking and 
  152. // receive handlers.
  153. //
  154. static Boolean DragItemsAreAcceptable ( DragReference theDrag )
  155. {
  156.     OSErr            theErr;
  157.     unsigned short    totalItems;
  158.     ItemReference    itemRef;
  159.     Boolean            bAcceptIt;
  160.     OSType            currOSType;
  161.     Size            flavorDataSize;
  162.     
  163.     
  164.     bAcceptIt = false;
  165.     
  166.     
  167.     // this app can only accept the drag of a single item
  168.     theErr = CountDragItems ( theDrag, &totalItems );
  169.     
  170.     if ( theErr == noErr && totalItems == 1 )
  171.     {
  172.         // get the reference number of the dragged item
  173.         theErr = GetDragItemReferenceNumber ( theDrag, 1, &itemRef );
  174.  
  175.         if ( theErr == noErr )
  176.         {
  177.             // check if the item is one of ours
  178.             flavorDataSize = sizeof ( OSType );
  179.             theErr = GetFlavorData ( theDrag, itemRef, kCreatorCode, &currOSType,
  180.                                         &flavorDataSize, 0 );
  181.             
  182.             //#if DEBUGGING
  183.             //if ( theErr ) DebugStr ( "\p GetFlavorData returned an error" );
  184.             //#endif
  185.             
  186.             if ( theErr == noErr ) 
  187.                 bAcceptIt = true;
  188.         }
  189.     }
  190.     return bAcceptIt;
  191. }
  192.  
  193.  
  194.  
  195. //
  196. // DragIsNotInSourceWindow returns true if the drag in progress
  197. // is not in the same window it originated in. This is called by
  198. // the tracking and receive handlers.
  199. //
  200. static Boolean DragIsNotInSourceWindow ( DragReference theDrag )
  201. {
  202.     DragAttributes currDragFlags;
  203.     
  204.     GetDragAttributes ( theDrag, &currDragFlags );
  205.     return !(currDragFlags & dragInsideSenderWindow);
  206. }
  207.  
  208.  
  209.  
  210. //
  211. // DragTracker is called by the drag manager whenever a drag is
  212. // over one of the application's windows. Upon entry, the Drag
  213. // Manager has already set the current port current to ‘theWindow’.
  214. pascal OSErr DragTracker ( DragTrackingMessage theMessage, WindowRef theWindow,
  215.                             void* handlerRefCon, DragReference theDrag )
  216. {
  217.     RgnHandle    tempRgn;
  218.     Boolean        mouseInList;
  219.     OSErr        err;
  220.     
  221.     err = noErr;
  222.  
  223.     switch ( theMessage )
  224.     {
  225.         case dragTrackingEnterHandler:
  226.             
  227.             // Any initialization for this window handler.
  228.             gHasAcceptableDrag = DragItemsAreAcceptable ( theDrag );
  229.             gHasHilitedList = false;
  230.             
  231.             // Let the drag manager know if we can't accept this drag
  232.             if ( !gHasAcceptableDrag )
  233.                 err = dragNotAcceptedErr;
  234.             break;
  235.             
  236.         case dragTrackingEnterWindow: 
  237.         case dragTrackingInWindow:
  238.         case dragTrackingLeaveWindow:
  239.             
  240.             // Highlighting of the window during a drag is done
  241.             // here.  Do it only if we can accept these items
  242.             // and we're not in the source window...
  243.             
  244.             if ( gHasAcceptableDrag )
  245.             {
  246.                 // Unless the mouse is leaving the visible area of the
  247.                 // window, check if it's in the window's content region
  248.                 
  249.                 mouseInList = false;
  250.                 if ( theMessage != dragTrackingLeaveWindow )
  251.                 {
  252.                     if ( DragIsNotInSourceWindow ( theDrag ) )
  253.                     {
  254.                         Point localPt;
  255.                     
  256.                         (void) GetDragMouse ( theDrag, &localPt, 0L );
  257.                         GlobalToLocal ( &localPt );
  258.                         mouseInList = PtInList ( localPt, GetWListRef ( theWindow ) );
  259.                     }
  260.                 }
  261.                 
  262.                 // If the mouse is in the list and it isn't hilited...
  263.                 if ( mouseInList && !gHasHilitedList )
  264.                 {
  265.                     Rect    nuSpaceRect;
  266.  
  267.                     GetListRect ( &nuSpaceRect, GetWListRef ( theWindow ) );
  268.  
  269.                     tempRgn = NewRgn ( );
  270.                     RectRgn ( tempRgn, &nuSpaceRect );
  271.  
  272.                     // ...draw the hilight...
  273.                     if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr )
  274.                         // ... and remember it's now hilited
  275.                         gHasHilitedList = mouseInList;
  276.                     
  277.                     DisposeRgn ( tempRgn );
  278.                 }
  279.                 
  280.                 // else if the mouse is not in the list and the window is hilited...
  281.                 else if ( !mouseInList && gHasHilitedList )
  282.                     // ...erase the hilight...
  283.                     if ( HideDragHilite ( theDrag ) == noErr )
  284.                         // ...remember that nothing is hilited
  285.                         gHasHilitedList = false;
  286.             }
  287.             break;
  288.  
  289.         // do nothing for the leaveHandler message
  290.         case dragTrackingLeaveHandler:
  291.             break;
  292.         
  293.         // let the drag manager know if we didn't recognize the message
  294.         default:
  295.             err = paramErr;
  296.     }
  297.     
  298.     return err;
  299. }
  300.  
  301.  
  302.  
  303. //
  304. // DragReceiver is called by the drag manager whenever an
  305. // item is dropped on one of the application's windows.
  306. //
  307. pascal OSErr DragReceiver ( WindowRef theWindow, void *handlerRefCon, DragReference theDrag )
  308. {
  309. #pragma unused (theWindow, handlerRefCon)
  310.  
  311.     Boolean            bMove;
  312.     ItemReference    itemRef;
  313.     Size            dataSize;
  314.     OSErr            err = noErr;
  315.     unsigned short    numItems, counter;
  316.     tDragData        theData;
  317.     int16            mouseDownModifiers, mouseUpModifiers;
  318.     
  319.     
  320.     
  321.     if (!DragItemsAreAcceptable(theDrag) || (gHasHilitedList == false))
  322.         return dragNotAcceptedErr;
  323.     
  324.                         
  325.     err = GetDragModifiers ( theDrag, nil, &mouseDownModifiers, &mouseUpModifiers );
  326.     if ( err )    goto CleanupAndBail;
  327.     bMove = !((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey));
  328.  
  329.     CountDragItems(theDrag, &numItems);
  330.     for (counter = 1; counter <= numItems; counter++)
  331.     {
  332.         err = GetDragItemReferenceNumber(theDrag, counter, &itemRef);
  333.         if (err != noErr)  goto CleanupAndBail;
  334.         
  335.         dataSize = sizeof ( tDragData );
  336.         err = GetFlavorDataSize ( theDrag, itemRef, kCreatorCode, &dataSize );
  337.         if ( dataSize == sizeof ( tDragData ) )
  338.             err = GetFlavorData ( theDrag, itemRef, kCreatorCode, &theData, &dataSize, 0 );
  339.         
  340.         
  341.         // We're going to first remove the drag hilite from here, because
  342.         // not doing so would result in a cleared out list rect, and when
  343.         // we go through the trackingLeaveWindow message up above, the
  344.         // HideDragHilite() call would draw the border again (since it's drawn
  345.         // in XOr mode).
  346.         
  347.         if ( gHasHilitedList )
  348.         {
  349.             if ( HideDragHilite ( theDrag ) == noErr)
  350.                 // remember that nothing is hilited
  351.                 gHasHilitedList = false;
  352.         }
  353.         
  354.         
  355.         if ( bMove )
  356.             err = MoveWindowFragment ( theData.theWindow, theData.theIndex, theWindow );
  357.         else
  358.             err = CopyWindowFragment ( theData.theWindow, theData.theIndex, theWindow );
  359.     }
  360.     
  361. CleanupAndBail:
  362.     return err;
  363. }
  364.  
  365.  
  366.  
  367. //
  368. // This is the Drag Manager's SendDataProc. It's called after a successful drag, when
  369. // the target application wants some data that was promised by the source application.
  370. //
  371. pascal OSErr SendDataProc ( FlavorType theType,  void* dragSendRefCon,
  372.                           ItemReference theItemRef, DragReference theDragRef )
  373.  
  374. {
  375.     // Gotcha: If we had just dragged out to the Finder, and we had a windowKind of
  376.     // 20, the system would have crashed by now. Why? The Finder uses a windowKind
  377.     // of 20, and thinks this is a drag from one of its windows. It then starts
  378.     // interpreting the window's refCon and inevitably doesn't like what it finds.
  379.     // A windowKind of 20 is now reserved for use by the system.
  380.     
  381.     
  382.     OSErr        result = noErr;
  383.     FSSpec        locationSpec;
  384.     tHeaderHan    theHeader = nil;
  385.     hdrHand        theResource = nil;
  386.  
  387.     
  388.     
  389.     // We use the file type for the HFSPromise flavor type
  390.     // and we don't handle any other pomised flavour types.
  391.     if ( theType != kCFragLibraryFileType )
  392.         cantGetFlavorErr;
  393.     
  394.     
  395.     result = CreateTemporaryFile ( &locationSpec );
  396.     if ( result == noErr )
  397.     {
  398.         OSErr            theErr;
  399.         int16            theIndex = 0;
  400.         int16            theRef, saveFile;
  401.         WindowRef        theWindow;
  402.         ListRef            theList;
  403.         tWindowInfoPtr    theInfo;
  404.         
  405.         theWindow = (WindowRef) dragSendRefCon;
  406.         theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
  407.         theList = GetWListRef ( theWindow );
  408.         
  409.         theHeader = (tHeaderHan) NewHandleClear ( sizeof ( tHeader ) );
  410.         theErr = MemError ( );
  411.         if ( theErr ) goto CleanupAndBail;
  412.         
  413.         (*theHeader)->version = 1;        // Current version number
  414.         
  415.         // First, we'll add the content to the file
  416.         while ( GetSelection ( theList, &theIndex ) )
  417.         {
  418.             int16    itemIndex;
  419.             
  420.             itemIndex = GetIndexFromNthWindowItem ( theWindow, theIndex );
  421.             theErr = CopyFragment ( (tHeaderHan) theInfo->dataHandle, &theInfo->fileSpec,
  422.                                         itemIndex, theHeader, &locationSpec );
  423.             if ( theErr )    goto CleanupAndBail;
  424.             theIndex++;
  425.         }
  426.         
  427.         theResource = (hdrHand) NewHandleClear ( offsetof ( cfrgHeader, arrayStart ) );
  428.         (*theResource)->version = 1;        // Current version number
  429.         
  430.         theErr = BuildResource ( (tHeaderHan) theHeader, (Handle) theResource );
  431.         if ( theErr )    goto CleanupAndBail;
  432.         
  433.         saveFile = CurResFile ( );
  434.         theRef = FSpOpenResFile ( &locationSpec, fsRdWrPerm );
  435.         // If the file is already open, it may not be the current resource file
  436.         UseResFile ( theRef );
  437.         AddResource ( (Handle) theResource, kCFragResourceType, kCFragResourceID, "\p" );
  438.         UpdateResFile ( theRef );
  439.         ReleaseResource ( (Handle) theResource );
  440.         CloseResFile ( theRef );
  441.         UseResFile ( saveFile );
  442.         
  443.         
  444.         // Now, set the flavor data of our kCFragLibraryFileType flavor 
  445.         // with an FSSpec to the new file.
  446.         result = SetDragItemFlavorData ( theDragRef, theItemRef, theType,
  447.                                             &locationSpec, sizeof ( FSSpec ), 0L );
  448.         
  449.     }
  450.     
  451.     
  452.     // Any errors? Return a cantGetFlavorErr
  453.     if ( result )
  454.         result = cantGetFlavorErr;
  455.         
  456.     return result;
  457.     
  458. CleanupAndBail:
  459.     
  460.     // We'll leave the temp file for the system to handle. It
  461.     // could still be useful, if only for debugging purposes.
  462.      if ( theHeader )
  463.          DisposeHandle ( (Handle) theHeader );
  464.      
  465.      
  466.      if ( theResource )
  467.      {
  468.         if ( IsAResource ( (Handle) theResource ) )
  469.             ReleaseResource ( (Handle) theResource );
  470.         else
  471.             DisposeHandle ( (Handle) theResource );
  472.     }
  473.     
  474.     return cantGetFlavorErr;
  475. }
  476.  
  477.  
  478.  
  479. //
  480. // This routine just promises the Drag Manager that we'll create a file
  481. // if the user drags out to the Finder
  482. //
  483. OSErr AddHFSPromise ( DragReference theDrag, ItemReference theItem )
  484. {
  485.     OSErr                theErr;
  486.     PromiseHFSFlavor    thePromise;
  487.     
  488.     
  489.     // Here's what we're going to promise to create in our send proc
  490.     thePromise.fileType = kCFragLibraryFileType;
  491.     thePromise.fileCreator = kFourQuestionMarks;
  492.     thePromise.fdFlags = 0;
  493.     thePromise.promisedFlavor = kCFragLibraryFileType;
  494.     
  495.     
  496.     // The promised flavor can be anything, just as long as we add a drag item
  497.     // that has the same type, and set it in our send proc. Failure to do this
  498.     // will cause the zoomback.
  499.     
  500.     theErr = AddDragItemFlavor ( theDrag, theItem, flavorTypePromiseHFS, &thePromise,
  501.                                         sizeof ( PromiseHFSFlavor ), 0L );
  502.     if ( theErr == noErr )
  503.         // Here's the promised flavor we're going to deliver
  504.         theErr = AddDragItemFlavor ( theDrag, theItem, kCFragLibraryFileType, nil, 0L, 0L );
  505.  
  506.     
  507.     return theErr;
  508. }
  509.  
  510.  
  511.  
  512.  
  513.